package org.doubango.ngn.h264

import android.annotation.TargetApi
import android.media.MediaCodec
import android.media.MediaFormat
import android.media.MediaMuxer
import android.os.Build
import android.os.Environment
import android.os.Message
import android.preference.PreferenceManager
import android.text.format.DateFormat
import android.text.format.Formatter
import android.util.Log
import com.evendai.loglibrary.Timber
import org.doubango.Whats
import org.doubango.ngn.NgnApplication
import org.doubango.ngn.utils.Aes
import org.doubango.ngn.utils.SDCardUtil
import org.greenrobot.eventbus.EventBus
import java.io.File
import java.io.IOException
import java.nio.ByteBuffer
import java.util.concurrent.Executors

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
class H264ToMp4Saver {

    @Suppress("SpellCheckingInspection")
    private lateinit var mMediaMuxer: MediaMuxer
    private var mByteBuffer: ByteBuffer? = null
    private var mTrackIndex: Int = 0
    @Suppress("SpellCheckingInspection")
    private var isMediaMuxerWriteData = false

    /** 开始录Mp4的时间，从这个时间开始算，10分钟后保存这个视频重新再录   */
    private var recordVideoStartTime = 0L
    private var startTime = 0L
    private var mFrameCount = 0L
    @Suppress("SpellCheckingInspection")
    private var createMediaMuxerOk: Boolean = false
    /** 10分钟   */
    private val tenMinutes = 1000L * 60 * 10

    /** SDCard路径 */
    private var mSdCardPath: String? = null
    /** 视频保存目录 */
    private lateinit var mVideoSaveDir: File
    /** 指示是否有SDCard  */
    private var hasSDCard = false
    private var mVideoFile: File? = null

    private val deleteThreadPool = Executors.newFixedThreadPool(1)
    @Suppress("PrivatePropertyName")
    private val _1K: Long = 1024L
    @Suppress("PrivatePropertyName")
    private val _1M: Long = 1024L * _1K
    @Suppress("PrivatePropertyName")
    private val _300M: Long = 300L * _1M

    init {
        checkHasSDCard()
        hasSDCard = mSdCardPath != null

        if (hasSDCard) {
            // 不没有系统权限时，则使用下面的目录来存储视频，此目录不需要任何存储权限
            val movieDirs = NgnApplication.getContext().getExternalFilesDirs(Environment.DIRECTORY_MOVIES)
            mVideoSaveDir = if (movieDirs.size == 1) {
                movieDirs[0]
            } else {
                movieDirs[1] // 数组中的第一个条目被视为主要外部存储，即不可移除的。第二个则为SDCard，可移除的
            }

            Timber.i("${mSdCardPath}的总存储空间：${Formatter.formatFileSize(NgnApplication.getContext(), SDCardUtil.getSdCardTotalSpace(mSdCardPath))}")
            Timber.i("${mSdCardPath}的可用储空间：${Formatter.formatFileSize(NgnApplication.getContext(), SDCardUtil.getSdCardUsableSpace(mSdCardPath))}")

//            mVideoSaveDir = File(mSdCardPath, "/DCIM/Video")

            if (!mVideoSaveDir.exists()) {
                if (!mVideoSaveDir.mkdirs()) {
                    Timber.e("创建视频保存目录失败!")
                    hasSDCard = false
                }
            }

            // 第一次在外置SDCard创建文件时花的时间比较长，第二次创建就快了，所以这里先创建一次，方便第二次创建时提速
            if (hasSDCard) {
                val testFile = File(mVideoSaveDir, "test.mp4")
                if (!testFile.exists()) {
                    if (testFile.createNewFile()) {
                        testFile.delete()
                    }
                } else {
                    testFile.delete()
                }
            }
        } else {
            Timber.e("没有检测到SDCard")
        }
    }

    fun checkHasSDCard() {
//        mSdCardPath = SDCardUtil.getSdCardPath()
        mSdCardPath = Environment.getExternalStorageDirectory()?.absolutePath
    }

    /** 保存H264为Mp4  */
    fun h264ToMp4(h264Data: H264Data) {
        if (!hasSDCard) return

        if (!createMediaMuxerOk) {
            createMp4File(h264Data.mediaFormat)
            if (!createMediaMuxerOk) return
        }

        try {
            if (recordVideoStartTime == 0L) {
                recordVideoStartTime = System.currentTimeMillis() // 记录刚开始保存视频时的时间
                startTime = recordVideoStartTime
            }
            val recordMp4ElapsedTime = System.currentTimeMillis() - recordVideoStartTime
            if (recordMp4ElapsedTime >= tenMinutes && h264Data.bufferInfo.flags == MediaCodec.BUFFER_FLAG_KEY_FRAME) {
                Log.i(TAG, "录制的视频大于了10分钟")
                saveMp4File()

                createMp4File(h264Data.mediaFormat)
                if (!createMediaMuxerOk) return
            }

            mByteBuffer!!.put(h264Data.h264Datas)
            mMediaMuxer.writeSampleData(mTrackIndex, mByteBuffer!!, h264Data.bufferInfo)
            isMediaMuxerWriteData = true
            mByteBuffer!!.rewind()
            mFrameCount++
        } catch (e: Exception) {
            Timber.e(e,"写mp4数据时出现异常")
            checkHasSDCard()
            saveMp4File()
        }
    }

    /** 确保有足够的可用空间 */
    private fun ensureEnoughSpace() {
        if (SDCardUtil.getSdCardUsableSpace(mSdCardPath) < _300M) {
            deleteThreadPool.execute {
                while (SDCardUtil.getSdCardUsableSpace(mSdCardPath) < _300M) {
                    deleteOldestFile(mVideoSaveDir)
                }
            }
        }
    }

    /** 删除指定目录下最老的文件 */
    private fun deleteOldestFile(dir: File?) {
        if (dir == null || !dir.exists()) return
        val listFiles = dir.listFiles()
        if (listFiles.isNullOrEmpty())  return
        val oldestFile = listFiles.minByOrNull {  it.lastModified() }!! // 修改时间最小的就是最老的
        Timber.d("空间小于100M，删除最老的文件：${oldestFile.absolutePath}")
        oldestFile.delete()
        EventBus.getDefault().post(Message.obtain(null, Whats.ON_ONE_FILE_DELETED, oldestFile))
    }

    fun saveMp4File() {
        if (!hasSDCard) return
        close()

        val elapsedTime = System.currentTimeMillis() - startTime
        if (mVideoFile != null && mVideoFile!!.exists()){
            if (mVideoFile!!.length() < _1K * 3) {
                val delete = mVideoFile!!.delete()
                Timber.i("删除了一个小于3K的文件，是否删除成功：$delete,它的名字为：${mVideoFile!!.absolutePath}")
            } else {
                val fps = if (mFrameCount == 0L) 0f else mFrameCount / (elapsedTime / 1000f)
                Timber.i("保存了一个视频文件，实际fps为：$fps，路径：${mVideoFile!!.absolutePath}")
                EventBus.getDefault().post(Message.obtain(null, Whats.ON_ONE_FILE_CREATED, mVideoFile))
            }
        }

        isMediaMuxerWriteData = false
        recordVideoStartTime = 0
        mFrameCount = 0
        startTime = 0
        mVideoFile = null
    }

    private fun createMp4File(mediaFormat: MediaFormat) {
        // 确保有足够的空间存储文件
        ensureEnoughSpace()

        val start = System.currentTimeMillis()
        if (!hasSDCard) return
        val defaultValue = "000000"
        var loginAccount = PreferenceManager.getDefaultSharedPreferences(NgnApplication.getContext()).getString(Aes.encrypt("ACCOUNT"), defaultValue)!!
        if (loginAccount != defaultValue) loginAccount = Aes.decrypt(loginAccount)
        val fileName = "${loginAccount}_${DateFormat.format("yyyy_MM_dd_kk_mm_ss", System.currentTimeMillis())}.mp4"
        mVideoFile = File(mVideoSaveDir, fileName)
        isMediaMuxerWriteData = false
        recordVideoStartTime = 0
        mFrameCount = 0
        startTime = 0
        try {
            mMediaMuxer = MediaMuxer(mVideoFile!!.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
            mTrackIndex = mMediaMuxer.addTrack(mediaFormat)
            mMediaMuxer.start()
            if (mByteBuffer == null) mByteBuffer = ByteBuffer.allocateDirect(mediaFormat.getInteger(MediaFormat.KEY_WIDTH) * mediaFormat.getInteger(MediaFormat.KEY_HEIGHT) * 3 shr 1)
            createMediaMuxerOk = true
        } catch (e: IOException) {
            checkHasSDCard()
            createMediaMuxerOk = false
            @Suppress("SpellCheckingInspection")
            Timber.e(e,"MediaMuxer创建失败!")
            if (mVideoFile != null && mVideoFile!!.exists()) {
                val isDelete = mVideoFile!!.delete()
                Timber.e("生成了一个空文件，是否删除成功：$isDelete, 文件名为：${mVideoFile!!.absolutePath}")
                mVideoFile = null
            }
        }
        Timber.i("创建文件花的时间：${System.currentTimeMillis() - start}")
    }

    fun close() {
        if (!createMediaMuxerOk) {
            return
        }

        createMediaMuxerOk = false
        try {
            if (isMediaMuxerWriteData) {
                isMediaMuxerWriteData = false
                mMediaMuxer.stop() // 如果我们没有写入东西时就关闭就会抛出异常
            }
            mMediaMuxer.release()
        } catch (e: Exception) {
            @Suppress("SpellCheckingInspection")
            Timber.w(e,"别慌，正常关闭MediaMuxer时出现异常")
        }
    }

    companion object {
        private val TAG = H264ToMp4Saver::class.java.simpleName
    }
}